home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Snippets / sscanf / mysscanf.c < prev    next >
Encoding:
Text File  |  1993-02-26  |  23.7 KB  |  631 lines  |  [TEXT/ALFA]

  1. /************************************************************************
  2. *                                                                       *
  3. * sscanf.c                                                              *
  4. *                                                                       *
  5. * Source code for a freely distributable sscanf() function.             *
  6. *                                                                       *
  7. * ********************************************************************* *
  8. *                                                                       *
  9. * Copyright (C) 1992, by Brent Burton                                   *
  10. *                                                                       *
  11. * Permission is granted for free use of this code for any purpose it is *
  12. * needed.  No strings attached.  However, out of courtesy, if you do    *
  13. * use this code, I'd appreciate a quick mention.  Enjoy!                *
  14. *                                                                       *
  15. * New news:                                                             *
  16. * If you come across bugs/undocumented features when you use this, I'd  *
  17. * appreciate a note explaining the error so I can fix it.  This new     *
  18. * version has gone through more testing, and I believe it returns the   *
  19. * same values as Think C's.  Actually, for all the various cases I      *
  20. * tested, it now works properly.                                        *
  21. * I officially do have all types but generic character sets implemented.*
  22. * In the last version I didn't implement character scan types.  They    *
  23. * are now.                                                              *
  24. *                                                                       *
  25. * Stepan Riha rewrote the floating point conversions and now the sscanf *
  26. * is *much* quicker than the original - instead of 3 times slower.      *
  27. * I have Stepan's permission to redistribute his modifications.         *
  28. *                                                                       *
  29. * Brent Burton, brentb@tamsun.tamu.edu    12/10/92                      *
  30. *                                                                       *
  31. * ********************************************************************* *
  32. *                                                                       *
  33. * What is the benefit to using this routine over, say, the original     *
  34. * routines?  Well, if you want your conversions to be faster, then      *
  35. * use this routine; here are some numbers to grok:                      *
  36. *                                                                       *
  37. *  (all number comparisons were done by scanning 10000 items on         *
  38. *   a Mac IIsi; all times given are in seconds.)                        *
  39. *                                                                       *
  40. *  result = sscanf( s, f, ...)                                          *
  41. *                                                                       *
  42. *   s             f             Think C time      Our time              *
  43. *  ---------------------------------------------------------            *
  44. *  0123456789     %s                 2.60             0.83              *
  45. *  0123456789     %10s               2.30             0.88              *
  46. *  40 A's         %s                 6.32             1.75              *
  47. *  40 A's         %40s               5.65             1.97              *
  48. *     1           %d                 1.72             0.75              *
  49. *     1.1         %d.%d              2.80             1.48              *
  50. *   12345         %d                 2.48             1.42              *
  51. *   12345         %ld                2.53             1.45              *
  52. *   12345         %lx                2.53             2.05              *
  53. *  ffffffff       %lx                3.30             3.07 *            *
  54. *    1.23         %f                 8.02             3.45              *
  55. *    123          %f                 7.30             3.10              *
  56. *    123e-2       %f                 8.67             4.10              *
  57. *    123e2        %f                 7.20             4.10              *
  58. *   1.234e56      %f                11.63             6.95              *
  59. *   1.234e123     %f                16.02             7.95              *
  60. *   1.234e123     %lf (double)      13.48             7.82              *
  61. *   1.234e-123    %lf               18.12             7.80              *
  62. *                                                                       *
  63. *  * This is the worst case input string for hex numbers.               *
  64. *    (FFFFFFFF can be slightly better than ffffffff.)                   *
  65. *                                                                       *
  66. * ********************************************************************* *
  67. *                                                                       *
  68. * Here are the type specifiers that this sscanf() recognizes:           *
  69. *                                                                       *
  70. * Format spec =  "%[*][width][size][type]"                              *
  71. * The meaning is:                                                       *
  72. *                                                                       *
  73. *  * - process the next item as usual, but don't save.  No argument is  *
  74. *      passed to sscanf() for this specifier.  Optional.                *
  75. *                                                                       *
  76. *  width - This series of digits specifies the absolute maximum number  *
  77. *          of characters to be read for this item specifier.  Fewer     *
  78. *          characters may be read, but no more.  Optional.              *
  79. *                                                                       *
  80. *  size - This value may be only one of:  l,L,h, or #.  This notes      *
  81. *         the size of the data type to use.  "l" (lower case L) marks   *
  82. *         a longsize (long) C type, "h" marks a halfsize (short) type,  *
  83. *         and the "#" notation is explained below.  If no size spec is  *
  84. *         given, the default C type is assumed.  For floating point     *
  85. *         numbers, "l" is double, h is short double, and L long double. *
  86. *                                                                       *
  87. *  type - This is the actual data type to scan.  Valid types are:       *
  88. *       d - decimal (signed or not).                                    *
  89. *       u - unsigned decimal (any sign seen in input is ignored and     *
  90. *           the value is stored as unsigned).                           *
  91. *       o - octal.  Octal digits (0-7) are read and stored.             *
  92. *       x - hexadecimal.  Hex digits (ex:  beef) are read and stored.   *
  93. *           Note:  no preceding "0x" is assumed.                        *
  94. *       c - Read a character, or read [width] characters.  The char-    *
  95. *           acter(s) read are stored into the array pointed to by the   *
  96. *           corresponding (char*) argument.  NOTE: you may use the      *
  97. *           "%4c" to read in a file type, rsrc type, creator, etc.  You *
  98. *           may also store it in a long:                                *
  99. *              OSType type;                                             *
  100. *              char s="APPLications";                                   *
  101. *              sscanf(s, "%4c", &foo);                                  *
  102. *       s - read characters and store in the string pointer passed to   *
  103. *           sscanf().  Whitespace terminates a string input, unless a   *
  104. *           width value was given.  Then sscanf() reads as many as      *
  105. *           possible up to the width limit.                             *
  106. *           For compatibility (for the most part) with Symantec's       *
  107. *           implementation, I added the '#' specifier for reading       *
  108. *           Pascal strings (actually, storing P strings).               *
  109. *           Note:  Since the first byte (offset zero) of a pascal       *
  110. *           string is the length, the string length is limited to 255.  *
  111. *           ex: Read a pascal string like:                              *
  112. *               int numWhat;                                            *
  113. *               Str255 what;  // or char what[STR_SIZE] or ...          *
  114. *               sscanf(" 49 dogs", "%d %#s", &numWhat, (char*)what);    *
  115. *       f - floating point. "lf" is double type, "hf" is "short float". *
  116. *           Other type spec variants (eEgG) are treated the same.       *
  117. *       % - The format specifier sequence "%%" means match one % in the *
  118. *           input string.                                               *
  119. *                                                                       *
  120. ************************************************************************/
  121.  
  122.  
  123. #include "sscanf.h"
  124.  
  125. static int c_value(char c, char spec);
  126.  
  127. /*********************************/
  128. /* Macros sscanf() needs.        */
  129. /* Note:  I changed the name     */
  130. /* of the isxxxxx() macros       */
  131. /* to Isxxxxx() to differentiate */
  132. /* mine from the Std. C.         */
  133. /*********************************/
  134.  
  135. #define Isdigit(c)   (( '0' <= (c)) && ( (c) <= '9'))  /* dec digit */
  136. #define Isodigit(c)   (( '0' <= (c)) && ( (c) <= '7'))  /* octal digit */
  137. #define Isxdigit(c)   ( (( '0' <= (c)) && ( (c) <= '9')) ||\
  138.             (( 'a' <= (c)) && ( (c) <= 'f')) ||\
  139.             (( 'A' <= (c)) && ( (c) <= 'F')) )  /* hex digit */
  140. #define Isspace(a)   (( (a) == ' ') || ( (a) == '\t'))
  141.  
  142. #define skip_spc(a)   while( Isspace(*a) ) (a)++;
  143.  
  144. #define TRUE 1
  145. #define FALSE 0
  146. #define EOF -1
  147.  
  148. #define REALLY_LONG -1        /* used to read any number of chars */
  149.  
  150. /*******************************************************************/
  151. /* If you do not want the floating point conversions compiled in,  */
  152. /* then uncomment the following line.                              */
  153. /*******************************************************************/
  154.  
  155. /* #define NO_FLOATING   /* add "/*" at beginning of line for fpt. */
  156.  
  157. #ifndef NO_FLOATING
  158. static double pow10(int i);  /* returns 10 raised to the i power */
  159. #endif
  160.  
  161. /****************************/
  162. /* Variable arguments stuff */
  163. /*******************************************************************/
  164. /* This is a modified version of the varargs definition from the   */
  165. /* Think C's "stdarg.h" header.  I changed the name of them all    */
  166. /* and deleted their va_end() since it was totally useless.        */
  167. /*                                                                 */
  168. /* VA_next returns the next value passed as an argument.  Yes, all */
  169. /* that typecasting is necessary to get around C's lvalue stuff.   */
  170. /* I'll be safe and say these macros are Copyright (C) Symantec.   */
  171. /*******************************************************************/
  172.  
  173. typedef void *VA_list;
  174.  
  175. #define VA_start(ap, arg1)    ap = &arg1 + 1
  176. #define VA_next(ap, type)    *(* (type **) &ap)++
  177.  
  178.  
  179. /********************************************************************/
  180.  
  181. #ifdef COMPARING  /* if comparing this to the original, rename this one */
  182. int mysscanf(const char *str, const char *fmt, ...)
  183. #else
  184. int   sscanf(const char *str, const char *fmt, ...)
  185. #endif
  186. {
  187.   register char   *s=(char*)str;  /* the pointers we manipulate */
  188.   register char   *f=(char*)fmt;
  189.   VA_list      ap;                /* the argument pointer */
  190.   int nstored=0;                  /* number of items we've scanned */
  191.   int width = REALLY_LONG;        /* "as many as needed" */
  192.   int base = 10;                  /* default base for numbers */
  193.   char longsize = FALSE,          /* flags for var sizes, */
  194.        longdbl  = FALSE,          /* for long double type */
  195.        halfsize = FALSE,
  196.        ignored  = FALSE,          /* and if we save result or not */
  197.        pasc     = FALSE,          /* "pasc" means a pascal string. */
  198.        sign     = FALSE,
  199.        c_spec   = FALSE;          /* was a "%c" type seen? */
  200.   char item_spec;                 /* type of item to read in */
  201.     
  202.   
  203.   VA_start(ap, fmt);              /* set ap to 1st arg after fmt */
  204.   
  205.   if (!*f) return 0;
  206.   if (!*s) return EOF;
  207.  
  208.   /********************** start of all code **********************/
  209.  
  210.   while ( *f && *s)          /* while we have chars to scan */
  211.   {
  212.     skip_spc(s);             /* skip any whitespace we may see */
  213.     skip_spc(f);
  214.     
  215.     /** if we run out of the input string, return EOF **/
  216.  
  217.     if ( *f && !*s) return (nstored ? nstored:EOF);
  218.     
  219.     if ( *f == '%')          /* beginning of format specifier */
  220.     {
  221.       char c;
  222.       
  223.       c = *++f;              /* read next char */
  224.       
  225.       if ( c == '*' )
  226.       {
  227.         ignored = TRUE;      /* scan but ignore this item */
  228.         c = *++f;            /* read next char */
  229.       }
  230.       
  231.       if ( Isdigit(c) )      /* was width specified? */
  232.       {
  233.         width = 0;
  234.         while (Isdigit(c))
  235.         {
  236.           width = width*10 + (c - '0');
  237.           c = *++f;          /* read next char */
  238.         } /* while */
  239.       } /* if */
  240.       
  241.       f--;                   /* backup; we increment it below */
  242.       
  243. nextchar:
  244.       if ( !*f ) return nstored;  /* end of string or format */
  245.       
  246.       switch ( c = *++f ) {
  247.         case '%':                /* we must match a percent */
  248.             if (*s != '%')
  249.               return ( !*s) ? EOF : nstored;    /* error - exit */
  250.             s++;             /* we matched it, so continue */
  251.             break;
  252.   
  253.         case 'l':            /* longsize */
  254.             longsize = TRUE;
  255.             longdbl = halfsize = pasc = FALSE;
  256.             goto nextchar;
  257.  
  258.         case 'h':            /* halfsize */
  259.             halfsize = TRUE;
  260.             longdbl = longsize = pasc = FALSE;
  261.             goto nextchar;
  262.             
  263.         case '#':            /* pascal string spec */
  264.             pasc = TRUE;
  265.             longdbl = halfsize = longsize = FALSE;
  266.             goto nextchar;
  267.   
  268.         case 'd':            /* decimal */
  269.             item_spec = 'd';
  270.             base = 10;
  271.             goto scan_int;
  272.             
  273.         case 'u':            /* unsigned int */
  274.             item_spec = 'u';
  275.             base = 10;
  276.             goto scan_int;
  277.   
  278.         case 'o':            /* octal */
  279.             item_spec = 'o';
  280.             base = 8;
  281.             goto scan_int;
  282.   
  283.         case 'x':            /* hex */
  284.             item_spec = 'x';
  285.             base = 16;
  286.             goto scan_int;
  287.  
  288.         case 'c':  c_spec = TRUE;     /* save raw chars - not a string */
  289.         case 's':                     /* C or Pascal string */
  290.             {
  291.               char *sp, *p_sp;        /* string ptr, pascal ptr */
  292.               int count=0;
  293.               
  294.               p_sp = sp = VA_next( ap, char*);
  295.               if (sp == (void*)0) return nstored; /* error if NULL */
  296.               
  297.               if (pasc && !c_spec) sp++; /* move past length byte */
  298.               
  299.               if (width == REALLY_LONG)  /* read what we need to */
  300.                 while ( *s && !Isspace(*s) )
  301.                 {
  302.                   count++;
  303.                   if ( ignored)
  304.                     s++;              /* don't copy if ignored */
  305.                   else
  306.                     *sp++ = *s++;
  307.                 }
  308.               else                    /* else read # of chars specified */
  309.               {
  310.                 while ( *s && (count++ < width))
  311.                   if (ignored)
  312.                     s++;              /* don't copy if ignored */
  313.                   else
  314.                     *sp++ = *s++;
  315.               } /* if else */
  316.               if (pasc )              /* store as Pascal string */
  317.                 *p_sp = (char)count;  /* store length */
  318.               else                    /* store as C string */
  319.                 if (!c_spec)          /*   if not a "%c" type.... */
  320.                   *sp = '\000';       /*     terminate new string */
  321.               f++;
  322.               if (!ignored) nstored++;
  323.               c_spec = pasc = FALSE;
  324.             }
  325.             break;
  326.  
  327. #ifndef NO_FLOATING  
  328. /****************************************/
  329. /*    Optimized floating point scaning    */
  330. /*  Written by Stepan Riha 10/15/92     */
  331. /****************************************/
  332.  
  333.         case 'L':            /* long double */
  334.             longdbl = TRUE;
  335.             longsize = halfsize = pasc = FALSE;
  336.             goto nextchar;
  337.  
  338.         case 'e':
  339.         case 'E':
  340.         case 'f':            /* float */
  341.         case 'g':
  342.         case 'G':
  343.          {
  344.             register char    *p;
  345.             register long double sum=0.0;
  346.             register int    sign = 1, exponent = 0, expsign = 1;
  347.  
  348.              if(*s == '-'){            /* get the sign, if any */
  349.                 sign = -1;
  350.                 s++;
  351.                 }
  352.             else if(*s == '+')
  353.                 s++;
  354.             p = s;                    /* get the integral part of number, if any */
  355.             while(*s>='0' && *s<='9')
  356.                 sum = sum*10.0 + *s++ - '0';
  357.             if(*s == '.'){            /* get the fractional part of the number, if any */
  358.                 double    factor = 1;
  359.                 s++;
  360.                 while(*s>='0' && *s<='9'){
  361.                     factor *= 10;
  362.                     sum += (*s++ -'0')/factor;
  363.                     }
  364.                 if(s==p+1)            /* there were no digits around the '.' */
  365.                     goto failure;
  366.                 }
  367.             if(s==p)                /* not a valid number */
  368.                 goto failure;
  369.             if(sign == -1)            /* adjust for sign */
  370.                 sum = -sum;
  371.             if(*s=='e' || *s=='E'){    /* get the expornent, if any */
  372.                 s++;
  373.                 if(*s == '-'){        /* get the sign of the exponent, if any */
  374.                     expsign = -1;
  375.                     s++;
  376.                     }
  377.                 else if(*s == '+')
  378.                     s++;
  379.                 p = s;                /* get the number of the exponent */
  380.                 while(*s>='0' && *s<='9')
  381.                     exponent = exponent*10 + *s++ - '0';
  382.                 if(s==p)            /* not a valid number */
  383.                     goto failure;
  384.                 exponent *= expsign;            /* adjust exponent for sign */
  385.                 }
  386.             if(exponent)                    /* adjust for exponent */
  387.                 sum *= pow10(exponent);
  388.         
  389.        /*** Store the flt-pt number in the correct size ***/
  390.             if (!ignored)
  391.             {
  392.               if (longdbl)
  393.               {  long double *ld;
  394.                 ld = VA_next(ap, long double*);
  395.                 if (ld == (void*)0) return nstored;
  396.                 *ld = (long double)sum;
  397.               }
  398.               else if (longsize)
  399.               {  double *dp;
  400.                 dp = VA_next(ap, double*);
  401.                 if (dp == (void*)0) return nstored;
  402.                 *dp = (double)sum;
  403.               }
  404.               else if (halfsize)
  405.               {  short double *sd;
  406.                 sd = VA_next(ap, short double*);
  407.                 if (sd == (void*)0) return nstored;
  408.                 *sd = (short double)sum;
  409.               }
  410.               else  /* normal floating pt */
  411.               {  float *ip;
  412.                 ip = VA_next(ap, float*);
  413.                 if (ip == (void*)0) return nstored;
  414.                 *ip = (float)sum;
  415.               }
  416.               nstored++;
  417.             } /* if !ignored */
  418.             f++;              /* get next format char */
  419.  
  420.           }  /* scan a floating pt num in */
  421.           break;
  422. failure:
  423.         if(nstored)
  424.             return nstored;
  425.         else
  426.             return ((*s) ? 0 : EOF);
  427.         return nstored;    
  428.         break;
  429. #endif  /* NO_FLOATING */
  430.             
  431.         default:            /* unknown item_spec */
  432.           return nstored;
  433.             break;
  434.  
  435.       } /* switch */
  436.       
  437.       goto reset;           /* reset the size and other flags */
  438.     }
  439.     else    /* we didn't read a format spec, but instead a string to match */
  440.     {
  441.       /*
  442.        * As long as the strings match, they aren't whitespace, and
  443.        * we have chars to left to check (*f and *s are not '\000'),
  444.        * keep checking.
  445.        */
  446.       while ( !(Isspace(*f) || Isspace(*s)) && *f && *s && *f!='%')
  447.       {  if (*f == *s)
  448.         {
  449.           f++; s++;
  450.         }
  451.         else
  452.           return nstored;  /* we found a difference - return */
  453.       } /* while */
  454.       if (!*f)        /* why did it terminate? */
  455.         return nstored;
  456.       if (!*s)
  457.         return EOF;
  458.       
  459.       /* since we exited the comparison loop, we found a difference */
  460.       goto reset;
  461.     } /* else */
  462.  
  463. scan_int: /*
  464.            * Once we get a 'd', 'u', 'x', or 'o' item spec, we
  465.            * come here to actually scan the number and convert it
  466.            * to the correctly sized data type (long, short or normal).
  467.            */
  468.     { /* scan_int block */
  469.       int count=width;
  470.       long sum=0;
  471.       
  472.       if ( *s == '-')
  473.       {
  474.         if ( item_spec == 'd')
  475.           { sign=TRUE; s++;}
  476.         else
  477.           { sign=FALSE; s++;}  /* else ignore sign for all else */
  478.       }
  479.       else
  480.         if ( *s == '+') { sign=FALSE; s++;}
  481.       
  482.       if ( !Isxdigit(*s))    /* if it's not *any* type of digit */
  483.         return nstored;
  484.  
  485.       /*
  486.        * Next read & convert the actual number.  This is
  487.        * split up into two parts to optimize for speed.
  488.        * Since for hex numbers we have to check extra
  489.        * characters (a-f), it takes more time and it's done
  490.        * differently.
  491.        */
  492.       if (item_spec != 'x')  /* if 'd','u', or 'o' */
  493.       {
  494.         if (count == REALLY_LONG)  /* read what we need to */
  495.           while (Isdigit(*s) && !Isspace(*s))
  496.             sum = sum*base + (*s++ -'0');
  497.         else                       /* else only scan count characters */
  498.           while ( Isdigit(*s) && !Isspace(*s)
  499.               && count-- > 0)
  500.             sum = sum*base + (*s++ -'0');
  501.       }
  502.       else  /* for 'x' */
  503.       {
  504.         if (count == REALLY_LONG)  /* read what we need to */
  505.           while ( Isxdigit(*s) && !Isspace(*s) )
  506.             sum = sum*base + c_value(*s++, item_spec);
  507.         else                       /* else only scan count characters */
  508.           while ( Isxdigit(*s) && !Isspace(*s) &&
  509.               count-- > 0)
  510.             sum = sum*base + c_value(*s++, item_spec);
  511.       }
  512.  
  513.       if (sign) sum = -sum;  /* negate if sign exists */
  514.       
  515.       /*
  516.        * Now the number is converted with sign and all; we
  517.        * need to determine the data type to save it as and
  518.        * then write it to that memory location.
  519.        */
  520.       if (!ignored)
  521.       {
  522.         if (longsize)
  523.         {  long int *ip;
  524.           ip = VA_next(ap, long int*);
  525.           if (ip == (void*)0) return nstored;
  526.           *ip = (long int) sum;
  527.         }
  528.         else if (halfsize)
  529.         {  short int *ip;
  530.           ip = VA_next(ap, short int*);
  531.           if (ip == (void*)0) return nstored;
  532.           *ip = (short int) sum;
  533.         }
  534.         else  /* normal ints */
  535.         {  int *ip;
  536.           ip = VA_next(ap, int*);
  537.           if (ip == (void*)0) return nstored;
  538.           *ip = (int) sum;
  539.         }
  540.         nstored++;
  541.       } /* if !ignored */
  542.       f++;              /* get next format char */
  543.     } /* scan_int block */
  544.  
  545.  
  546. reset:
  547.     /*----------------------------------------
  548.     ** At this point, we've either matched a string in str and fmt
  549.     ** or we correctly scanned an item in from str.
  550.     ** We now continue, looking for another item or chars to match.
  551.     **  (i.e., continue with while loop )
  552.     ** We also need to reset our flags...
  553.     **----------------------------------------*/
  554.      c_spec = ignored = halfsize = longdbl =
  555.             longsize = pasc = sign = FALSE;
  556.      base = 10;
  557.      width = REALLY_LONG;
  558.     
  559.   } /* while() */
  560.   
  561.   return nstored;
  562. } /* sscanf() */
  563.  
  564.  
  565.  
  566. /*
  567.  * Given the item specifier, what is the number value of the
  568.  * character in c?
  569.  */
  570.  
  571. static int c_value( char c, char spec)
  572. {
  573.  
  574.   if (spec == 'x')
  575.   {
  576.     switch (c) {    /* simple, but does the job -- 2.75 secs */
  577.       case 'A':
  578.       case 'a': return 10;
  579.       case 'B':
  580.       case 'b': return 11;
  581.       case 'C':
  582.       case 'c': return 12;
  583.       case 'D':
  584.       case 'd': return 13;
  585.       case 'E':
  586.       case 'e': return 14;
  587.       case 'F':
  588.       case 'f': return 15;
  589.       default:  return c-'0';    /* for digits */
  590.     } /* switch */
  591.   }
  592.   else
  593.     return ( c-'0');  /* for 'd', 'u', and 'o' specs */
  594. } /* c_value() */
  595.  
  596.  
  597.  
  598. #ifndef NO_FLOATING
  599. /*
  600.     pow10    returns the ith power of 10
  601.     Written by Stepan Riha 10/15/92
  602. */
  603.  
  604. static double pow10(register int i)
  605. {
  606.     register double result = 1.0, factor;
  607.  
  608.     if (i == 0) return result;
  609.     
  610.     if (i <  0){
  611.         factor = 0.1;
  612.         i = -i;
  613.         }
  614.     else
  615.         factor = 10.0;
  616.  
  617.     goto multiply;
  618.     
  619.     while(i){
  620.         factor *= factor;
  621. multiply:
  622.         if(i&0x1L)
  623.             result *= factor;
  624.         i>>=1;
  625.         }
  626.   return result;
  627. } /* pow10() */
  628.  
  629. #endif    /* NO_FLOATING */
  630.  
  631.